package com.easyj.base.common.imp;

import java.util.LinkedList;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import com.easyj.admin.db.model.ImpResult;
import com.easyj.admin.sys.service.ImpResultService;
import com.easyj.base.enums.ImpEnum;
import com.easyj.base.exception.ImpValidException;
import com.easyj.base.storage.service.StorageService;
import com.easyj.base.util.SpringContextHolder;

/**
 *   使用excel导入行数据
 *   通用导入实现abstract类，
 * 
 * @author hjf
 *
 * @param <T>
 */
public abstract class ImpExcutor<T> {
	
	protected Workbook wb=null;
	protected Sheet sheet;
	protected String fileType="";
	
	@SuppressWarnings("rawtypes")
	private Drawing drawing;
	
	private ClientAnchor anchor;
	private CreationHelper factory;
	private CellStyle errorStyle;
		
	protected List<ImpField> fields;
	private int count=0;
	private ImpResult impResult=null;
	private List<Integer> sucessRowList=new LinkedList<Integer>();
	
	//是否保存标识
	private boolean saveImpResultFlag=true;
	//service
	private ImpResultService impResultService;
	private StorageService storageService;
	
	public Logger log =  LoggerFactory.getLogger(getClass());
	
	protected ImpDefine impDefine;
	
	public ImpExcutor(String name,Long operatorId) {
		
		storageService =SpringContextHolder.getBean(StorageService.class);
		impResultService=SpringContextHolder.getBean(ImpResultService.class);
		
		impResult=new ImpResult();
		//set opratorId---
		impResult.setName(name);
		impResult.setOperatorId(operatorId);
		impResult.setStartTime(new Date());
	}
	
	public ImpExcutor(String name,Long operatorId,boolean saveImpResultFlag ) {
		   this.saveImpResultFlag=saveImpResultFlag;
	       
		    storageService =SpringContextHolder.getBean(StorageService.class);
			impResultService=SpringContextHolder.getBean(ImpResultService.class);
			
			impResult=new ImpResult();
			//set opratorId---
			impResult.setName(name);
			impResult.setOperatorId(operatorId);
			impResult.setStartTime(new Date());
		
	}
	
	protected void readExcelFile(MultipartFile excelfile) throws Exception{
		if(excelfile == null ||excelfile.getSize()<=0){
			throw new Exception("导入文件为空！");
		}
		
		String filename=excelfile.getOriginalFilename();
		int inx=filename.lastIndexOf(".");
		fileType=filename.substring(inx+1, filename.length());
		
		if(fileType.equals("xls")){
		    wb=new HSSFWorkbook(excelfile.getInputStream());
		}
		else if(fileType.equals("xlsx")){
		     wb=new XSSFWorkbook(excelfile.getInputStream());
		}
		else{
			throw new Exception("导入文件类型不符合要求！");
			
		}		
		sheet=wb.getSheetAt(0);
		
		//FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
		//CellValue cellValue = evaluator.evaluate(cell);
		
		errorStyle = wb.createCellStyle(); 
		errorStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.ORANGE.getIndex());
		errorStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
		
	    factory = wb.getCreationHelper();
		drawing = sheet.createDrawingPatriarch();
		anchor = factory.createClientAnchor();
		
	}
	
	/**
	 *  取得关校验实体
	 * @param row
	 * @return
	 * @throws ImpValidException
	 */
	protected  abstract T getAndValidateBean(Row row) throws ImpValidException ;
	
	
	
	/**
	 * 保存
	 * @param bean
	 * @return
	 */
	protected  abstract int save(T bean) throws Exception;
	
	

    public ImpResult initImport(MultipartFile excelfile,String defineXml) throws Exception{
    	try {
	    	this.readExcelFile(excelfile);
	    	impDefine=ImpUtils.getImpDefine(defineXml);
	    	fields=impDefine.getImpFields();
	    	boolean flag=ImpUtils.processImpDefine(sheet, impDefine);//get position
	    	if(!flag) {
	    		throw new Exception("导入文件的表头不符合要求，请下载导入模板按模板要求录入数据！");
	    	}
			int end=sheet.getLastRowNum();
			count=end-impDefine.getDefineRowNum(); //get count 
			if(count==0) {
				throw new Exception("导入文件为空！");
			}
			
			
				impResult.setCount(count);
				impResult.setFailure(0);
				impResult.setSuccess(0);
				impResult.setStatus(ImpEnum.Started.getKey());	
				
				//save to table------
				if(saveImpResultFlag) {
				   impResultService.insertEntity(impResult);
			     }
			
			return impResult;
    	}catch(Exception ex) {
    		 try {
    		 		if(wb!=null)  
    				    wb.close();
    			  } catch (IOException e) {
    				
    			  }
    		 throw ex;
    	}
    }
    
    
   public void execImport() {
	 //异步执行 .....
	   int errorRow=0;
	   int successRow=0;
	   int emptyRow=0;
	   int start=impDefine.getDefineRowNum()+1;
 	   for(int i=0;i<count;i++){
 		    boolean successflag=true;
 			Row r=null;
 			int rowninx=start+i;
			r=sheet.getRow(rowninx);
			if(r==null) {
				emptyRow++;
				continue;
			}
			T bean=null;
			try {
			   bean=getAndValidateBean(r);
			}catch(ImpValidException ve) {
				successflag=false;
								
				//add Comment------------
				setRowComment(ve,r);			
			}
							
			if(bean!=null){
				try {
				   save(bean);
				  //成功后
				  sucessRowList.add(0,rowninx);//后面的放在前
				  
				}catch(ImpValidException ve) {
					successflag=false;//add Comment------------
					setRowComment(ve,r);			
				}catch(Exception se) {
					successflag=false;
					Cell cx0=r.getCell(0);
			    	if(cx0==null){
			    		cx0=r.createCell(0);
			    	}
			    	setCellComment(cx0,"保存错误！"+se.getMessage());
				}
			}
 			
			if(successflag) {
				successRow++;
			}else {
				errorRow++;
			}
			
 			
 			//save import result  10 次更新一次
	 			if(i>0 && i%10==0 && i!=count-1) { 
	 				 try {
			 			impResult.setCount(count-emptyRow);
			 			impResult.setFailure(errorRow);
			 			impResult.setSuccess(successRow);
			 			if(saveImpResultFlag) {
			 			   impResultService.updateEntity(impResult);
			 			}
	 				 }catch(Exception ex) {
	 					log.error("更新导入结果出错！", ex);
	 				 }
	 			}
				
 	  }
 	   
 	   //finish....
 	   String reultUrl=null;
 	  if(errorRow>0) {
 		 try{
 	  		//只保留错的行
 	  		for(Integer inx:sucessRowList){
 	  			ImpUtils.removeRow(sheet,inx);
 	  		} 	  		
 	  		reultUrl=storageService.saveExcelImpResult(wb, fileType);
 	  		 	      	
 	      }catch(Exception ex){   
 	      	//ex.printStackTrace();
 	      }
 	  }
 	  
 	   
 	  //save import result
 	  try {
			impResult.setCount(count-emptyRow);
			impResult.setFailure(errorRow);
			impResult.setSuccess(successRow);
			impResult.setStatus(ImpEnum.Finished.getKey());	
			impResult.setFinishTime(new Date());
			impResult.setResultFile(reultUrl);
			if(saveImpResultFlag) {
			   impResultService.updateEntity(impResult);
			}
	  }catch(Exception ex) {
		  log.error("更新导入结果出错！", ex);
	  }
 	  
 	  try {
 		if(wb!=null)  
		    wb.close();
	  } catch (IOException e) {
		
	  }
 	  //执行导入之后的处理------
 	  afterImport();
   }
   
   protected void afterImport() {
	   
   }
   
   private void setRowComment(ImpValidException ve, Row r) {
	   Map<String,String> errmap=ve.getFieldErrorsMap();
		if(errmap!=null&& errmap.size()>0&&r!=null){
		    try{
		    	for(Map.Entry<String, String> entry:errmap.entrySet()){
		    		ImpField errf=getFieldByPath(entry.getKey());
		    		if(errf!=null &&errf.getPosition()!=null ) {
				    	Cell cx=r.getCell(errf.getPosition());
				    	if(cx==null){
				    		cx=r.createCell(errf.getPosition());
				    	}
				    	setCellComment(cx,entry.getValue());
		    		}	
		    	}
		    	
		    }catch(Exception el){
		    	//.printStackTrace();
		    }
		}   	
   }
   private void setCellComment(Cell cx,String comment) {
	   if(cx!=null){
	    	if(cx.getCellComment()!=null){
	    		Comment com=cx.getCellComment();
	    		RichTextString str1 = factory.createRichTextString(com.getString()+"\r\n"+comment);
	    		com.setString(str1);
	    		cx.setCellComment(com);
	    	}else {
		    	Comment comment1 = drawing.createCellComment(anchor);
				RichTextString str1 = factory.createRichTextString(comment);
				comment1.setString(str1);
				comment1.setVisible(false);
				cx.setCellComment(comment1);
	    	}	
   	}
   }
   
   protected ImpField getFieldByPath(String path){
	   for(ImpField f:fields){
		   if(f.getName().equals(path)){
				   return f;
			  
		   }
	   }
	   
	   return null;
   }
   /**
     return fields
    */
   public List<ImpField> getFields(){
	   return fields;
   }

}
